package net.apexes.wsonrpc.client;

import net.apexes.wsonrpc.client.support.SimpleWebsocketConnector;
import net.apexes.wsonrpc.core.BinaryWrapper;
import net.apexes.wsonrpc.core.IdGenerater;
import net.apexes.wsonrpc.core.WsonrpcConfig;
import net.apexes.wsonrpc.core.WsonrpcConfigBuilder;
import net.apexes.wsonrpc.core.WsonrpcLogger;
import net.apexes.wsonrpc.json.JsonImplementor;

import java.util.concurrent.Executor;

/**
 * @author <a href="mailto:hedyn@foxmail.com">HeDYn</a>
 */
public final class WsonrpcClientBuilder extends WsonrpcConfigBuilder {

    public static WsonrpcClientBuilder create() {
        return new WsonrpcClientBuilder();
    }

    private Executor executor;
    private WebsocketConnector connector;
    private PingProvider pingProvider;
    /**
     * 连接超时时间，单位ms
     */
    private int connectTimeout = 5 * 1000;
    /**
     * 读取数据超时时间，单位ms
     */
    private int soTimeout = 0;
    /**
     * 心跳间隔时间，单位ms
     */
    private int heartbeatInterval = 30 * 1000;
    /**
     * 断开连接的心跳周期数，即当有设定值个心跳周期没有接收到数据时将自动断开连接，单位ms。为0时表示不自动断开连接。
     */
    private int heartbeatExpireCycle = 0;
    /**
     * 自动重连间隔时间，单位ms
     */
    private int reconnectIntervalMin = 30 * 1000;
    private int reconnectIntervalMax = 30 * 1000;
    private int reconnectIntervalStep = 0;

    private WsonrpcClientBuilder() {
    }

    @Override
    public WsonrpcClientBuilder json(JsonImplementor jsonImpl) {
        super.json(jsonImpl);
        return this;
    }

    @Override
    public WsonrpcClientBuilder wrapper(BinaryWrapper binaryWrapper) {
        super.wrapper(binaryWrapper);
        return this;
    }

    @Override
    public WsonrpcClientBuilder idGenerater(IdGenerater idGenerater) {
        super.idGenerater(idGenerater);
        return this;
    }

    @Override
    public WsonrpcClientBuilder logger(WsonrpcLogger wsonrpcLogger) {
        super.logger(wsonrpcLogger);
        return this;
    }

    public WsonrpcClientBuilder executor(Executor executor) {
        this.executor = executor;
        return this;
    }

    public WsonrpcClientBuilder connector(WebsocketConnector connector) {
        this.connector = connector;
        return this;
    }

    public WsonrpcClientBuilder pingProvider(PingProvider provider) {
        this.pingProvider = provider;
        return this;
    }

    public WsonrpcClientBuilder timeoutSeconds(int connectTimeoutSec) {
        return timeoutMillis(connectTimeoutSec * 1000);
    }

    public WsonrpcClientBuilder timeoutSeconds(int connectTimeoutSec, int soTimeoutSec) {
        return timeoutMillis(connectTimeoutSec * 1000, soTimeoutSec * 1000);
    }

    public WsonrpcClientBuilder timeoutMillis(int connectTimeoutMs) {
        this.connectTimeout = connectTimeoutMs;
        return this;
    }

    public WsonrpcClientBuilder timeoutMillis(int connectTimeoutMs, int soTimeoutMs) {
        this.connectTimeout = connectTimeoutMs;
        this.soTimeout = soTimeoutMs;
        return this;
    }

    /**
     * 设置心跳间隔时间
     * @param sec 间隔秒数
     * @return 返回当前实例
     */
    public WsonrpcClientBuilder heartbeatSeconds(int sec) {
        return heartbeatMillis(sec * 1000);
    }

    /**
     * 设置心跳间隔时间
     * @param sec 间隔秒数
     * @param expireCycle 触发断开连接的无接收数据心跳周期数
     * @return 返回当前实例
     */
    public WsonrpcClientBuilder heartbeatSeconds(int sec, int expireCycle) {
        return heartbeatMillis(sec * 1000, expireCycle);
    }

    /**
     * 设置心跳间隔时间
     * @param ms 间隔毫秒数
     * @return 返回当前实例
     */
    public WsonrpcClientBuilder heartbeatMillis(int ms) {
        return heartbeatMillis(ms, 0);
    }

    /**
     * 设置心跳间隔时间
     * @param ms 间隔毫秒数
     * @param expireCycle 触发断开连接的无接收数据心跳周期数
     * @return 返回当前实例
     */
    public WsonrpcClientBuilder heartbeatMillis(int ms, int expireCycle) {
        if (ms < 0) {
            throw new IllegalArgumentException("ms is wrong.");
        }
        if (heartbeatExpireCycle < 0) {
            throw new IllegalArgumentException("heartbeatExpireCycle is wrong.");
        }
        this.heartbeatInterval = ms;
        this.heartbeatExpireCycle = expireCycle;
        return this;
    }

    /**
     * 等间隔重连的间隔
     * @param sec 间隔秒数
     * @return 返回当前实例
     */
    public WsonrpcClientBuilder reconnectSeconds(int sec) {
        return reconnectMillis(sec * 1000);
    }

    /**
     * 等间隔重连的间隔
     * @param ms 间隔毫秒数
     * @return 返回当前实例
     */
    public WsonrpcClientBuilder reconnectMillis(int ms) {
        return reconnectMillis(ms, ms, 0);
    }

    /**
     * 逐渐递增的重连间隔
     * @param min 最少间隔秒数
     * @param max 最大间隔秒数
     * @param step 每次递增的值
     * @return 返回当前实例
     */
    public WsonrpcClientBuilder reconnectSeconds(int min, int max, int step) {
        return reconnectMillis(min * 1000, max * 1000, step * 1000);
    }

    /**
     * 逐渐递增的重连间隔
     * @param min 最少间隔毫秒数
     * @param max 最大间隔毫秒数
     * @param step 每次递增的值
     * @return 返回当前实例
     */
    public WsonrpcClientBuilder reconnectMillis(int min, int max, int step) {
        if (min > max) {
            throw new IllegalArgumentException("min or max is wrong.");
        }
        if (step < 0) {
            throw new IllegalArgumentException("step is wrong.");
        }
        this.reconnectIntervalMin = min;
        this.reconnectIntervalMax = max;
        this.reconnectIntervalStep = step;
        return this;
    }

    /**
     * 创建 WsonrpcClient 实例
     * @param url 连接URL
     * @return 返回 WsonrpcClient 实例
     */
    public WsonrpcClient client(String url) {
        return client(new FixedUrlProvider(url));
    }

    /**
     * 创建 WsonrpcClient 实例
     * @param urlProvider 提供连接URL的实例
     * @return 返回 WsonrpcClient 实例
     */
    public WsonrpcClient client(UrlProvider urlProvider) {
        if (urlProvider == null) {
            throw new NullPointerException("urlProvider is null.");
        }
        if (connector == null) {
            connector = new SimpleWebsocketConnector(connectTimeout, soTimeout);
        }
        if (pingProvider == null) {
            pingProvider = new PingProvider() {
                @Override
                public byte[] payload() {
                    return new byte[0];
                }
            };
        }
        WsonrpcClientConfig config = new ConfigImpl(build(), executor, connector, urlProvider, pingProvider,
                heartbeatInterval, heartbeatExpireCycle, reconnectIntervalMin, reconnectIntervalMax, reconnectIntervalStep);
        return new WsonrpcClientImpl(config);
    }

    /**
     *
     * @author <a href="mailto:hedyn@foxmail.com">HeDYn</a>
     *
     */
    private static class ConfigImpl extends WsonrpcConfigImpl implements WsonrpcClientConfig {

        private final Executor executor;
        private final WebsocketConnector connector;
        private final UrlProvider urlProvider;
        private final PingProvider pingProvider;
        private final int heartbeatInterval;
        private final int heartbeatExpireCycle;
        private final int reconnectIntervalMin;
        private final int reconnectIntervalMax;
        private final int reconnectIntervalStep;

        private ConfigImpl(WsonrpcConfig config,
                           Executor executor,
                           WebsocketConnector connector,
                           UrlProvider urlProvider,
                           PingProvider pingProvider,
                           int heartbeatInterval,
                           int heartbeatExpireCycle,
                           int reconnectIntervalMin,
                           int reconnectIntervalMax,
                           int reconnectIntervalStep) {
            super(config);
            this.executor = executor;
            this.connector = connector;
            this.urlProvider = urlProvider;
            this.pingProvider = pingProvider;
            this.heartbeatInterval = heartbeatInterval;
            this.heartbeatExpireCycle = heartbeatExpireCycle;
            this.reconnectIntervalMin = reconnectIntervalMin;
            this.reconnectIntervalMax = reconnectIntervalMax;
            this.reconnectIntervalStep = reconnectIntervalStep;
        }

        @Override
        public String getUrl() {
            return urlProvider.url();
        }

        @Override
        public Executor getExecutor() {
            return executor;
        }

        @Override
        public WebsocketConnector getWebsocketConnector() {
            return connector;
        }

        @Override
        public PingProvider getPingProvider() {
            return pingProvider;
        }

        @Override
        public int getHeartbeatInterval() {
            return heartbeatInterval;
        }

        @Override
        public int getHeartbeatExpireCycle() {
            return heartbeatExpireCycle;
        }

        @Override
        public int getReconnectIntervalMin() {
            return reconnectIntervalMin;
        }

        @Override
        public int getReconnectIntervalMax() {
            return reconnectIntervalMax;
        }

        @Override
        public int getReconnectIntervalStep() {
            return reconnectIntervalStep;
        }
    }

    private static class FixedUrlProvider implements UrlProvider {

        private final String url;

        private FixedUrlProvider(String url) {
            this.url = url;
        }

        @Override
        public String url() {
            return url;
        }
    }
}
